package com.d3.pomodoro.component

import android.content.Context
import android.graphics.*
import android.graphics.drawable.Drawable
import android.text.TextUtils
import android.util.AttributeSet
import android.util.TypedValue
import android.view.Gravity
import android.view.MotionEvent
import android.view.View
import androidx.core.view.MotionEventCompat
import com.d3.pomodoro.R

class SimpleButton(context: Context, attrs: AttributeSet) : View(context, attrs) {

    companion object {
        // 3种外形模式：圆角矩形、圆弧、直角矩形
        val SHAPE_ROUND_RECT = 101
        val SHAPE_ARC = 102
        val SHAPE_RECT = 103
        // 类型模式：正常、可选中、图标选中消失、图标选中切换
        val MODE_NORMAL = 201
        val MODE_CHECK = 202
        val MODE_ICON_CHECK_INVISIBLE = 203
        val MODE_ICON_CHECK_CHANGE = 204
    }

    // 显示外形
    private var mTagShape = SHAPE_ROUND_RECT
    // 显示类型
    private var mTagMode = MODE_NORMAL
    // 画笔
    private var mPaint: Paint? = null
    // 背景色
    private var mBgColor = Color.WHITE
    // 边框颜色
    private var mBorderColor = Color.parseColor("#ff333333")
    // 原始标签颜色
    private var mTextColor = Color.parseColor("#ff666666")
    // 选中状态背景色
    private var mBgColorChecked = Color.WHITE
    // 选中状态边框颜色
    private var mBorderColorChecked = Color.parseColor("#ff333333")
    // 选中状态字体颜色
    private var mTextColorChecked = Color.parseColor("#ff666666")
    // 遮罩颜色
    private var mScrimColor = Color.argb(0x66, 0xc0, 0xc0, 0xc0)
    // 字体大小
    private var mTextSize: Float = 0.toFloat()
    // 字体宽度和高度
    private var mFontLen: Int = 0
    private var mFontH: Int = 0
    private var mFontLenChecked: Int = 0
    // 基线偏移距离
    private var mBaseLineDistance: Float = 0.toFloat()
    // 边框大小
    private var mBorderWidth: Float = 0.toFloat()
    // 边框角半径
    private var mRadius: Float = 0.toFloat()
    // 内容
    private var mText: String? = null
    // 选中时内容
    private var mTextChecked: String? = null
    // 显示的文字
    private var mShowText: String? = null
    // 字体水平空隙
    private var mHorizontalPadding: Int = 0
    // 字体垂直空隙
    private var mVerticalPadding: Int = 0
    // 边框矩形
    private var mRect: RectF? = null
    // 装饰的icon
    private var mDecorateIcon: Drawable? = null
    // 变化模式下的icon
    private var mDecorateIconChange: Drawable? = null
    // 设置图标的位置，只支持左右两边
    private var mIconGravity = Gravity.LEFT
    // icon和文字间距
    private var mIconPadding = 0
    // icon大小
    private var mIconSize = 0
    // 是否选中
    private var mIsChecked = false
    // 是否自动切换选中状态，不使能可以灵活地选择切换，通过用于等待网络返回再做切换
    private var mIsAutoToggleCheck = false
    // 是否被按住
    private var mIsPressed = false

    init {
        _init(context, attrs)
    }

    private fun _init(context: Context, attrs: AttributeSet?) {
        mBorderWidth = dp2px(context, 0.5f)
        mRadius = dp2px(context, 5f)
        mHorizontalPadding = dp2px(context, 5f).toInt()
        mVerticalPadding = dp2px(context, 5f).toInt()
        mIconPadding = dp2px(context, 3f).toInt()
        mTextSize = sp2px(context, 14f)

        if (attrs != null) {
            val a = context.obtainStyledAttributes(attrs, R.styleable.SimpleButton)
            try {
                mTagShape =
                    a.getInteger(R.styleable.SimpleButton_sb_shape, SimpleButton.SHAPE_ROUND_RECT)
                mTagMode = a.getInteger(R.styleable.SimpleButton_sb_mode, MODE_NORMAL)
                if (mTagMode == MODE_CHECK || mTagMode == MODE_ICON_CHECK_INVISIBLE || mTagMode == MODE_ICON_CHECK_CHANGE) {
                    mIsAutoToggleCheck = true
                    mIsChecked = a.getBoolean(R.styleable.SimpleButton_sb_checked, false)
                    mDecorateIconChange = a.getDrawable(R.styleable.SimpleButton_sb_icon_change)
                }
                mIsAutoToggleCheck =
                    a.getBoolean(R.styleable.SimpleButton_sb_auto_check, mIsAutoToggleCheck)

                mText = a.getString(R.styleable.SimpleButton_sb_text)
                mTextChecked = a.getString(R.styleable.SimpleButton_sb_text_check)
                mTextSize = a.getDimension(R.styleable.SimpleButton_sb_text_size, mTextSize)
                mBgColor = a.getColor(R.styleable.SimpleButton_sb_bg_color, Color.WHITE)
                mBorderColor = a.getColor(
                    R.styleable.SimpleButton_sb_border_color,
                    Color.parseColor("#ff333333")
                )
                mTextColor = a.getColor(
                    R.styleable.SimpleButton_sb_text_color,
                    Color.parseColor("#ff666666")
                )
                mBgColorChecked = a.getColor(R.styleable.SimpleButton_sb_bg_color_check, mBgColor)
                mBorderColorChecked =
                    a.getColor(R.styleable.SimpleButton_sb_border_color_check, mBorderColor)
                mTextColorChecked =
                    a.getColor(R.styleable.SimpleButton_sb_text_color_check, mTextColor)
                mBorderWidth =
                    a.getDimension(R.styleable.SimpleButton_sb_border_width, mBorderWidth)
                mRadius = a.getDimension(R.styleable.SimpleButton_sb_border_radius, mRadius)
                mHorizontalPadding = a.getDimension(
                    R.styleable.SimpleButton_sb_horizontal_padding,
                    mHorizontalPadding.toFloat()
                ).toInt()
                mVerticalPadding = a.getDimension(
                    R.styleable.SimpleButton_sb_vertical_padding,
                    mVerticalPadding.toFloat()
                ).toInt()
                mIconPadding =
                    a.getDimension(R.styleable.SimpleButton_sb_icon_padding, mIconPadding.toFloat())
                        .toInt()
                mDecorateIcon = a.getDrawable(R.styleable.SimpleButton_sb_icon)
                mIconGravity = a.getInteger(R.styleable.SimpleButton_sb_gravity, Gravity.LEFT)
            } finally {
                a.recycle()
            }
        }

        if (mTagMode == MODE_ICON_CHECK_CHANGE && mDecorateIconChange == null) {
            throw RuntimeException("You must set the drawable by 'tag_icon_change' property in MODE_ICON_CHECK_CHANGE mode")
        }
        // 如果没有图标则把 mIconPadding 设为0
        if (mDecorateIcon == null && mDecorateIconChange == null) {
            mIconPadding = 0
        }
        mRect = RectF()
        mPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        setClickable(true)
    }

    private fun _adjustText(maxWidth: Int): Int {
        if (mPaint!!.textSize != mTextSize) {
            mPaint!!.textSize = mTextSize
            val fontMetrics = mPaint!!.fontMetrics
            // 文字高度
            mFontH = (fontMetrics.descent - fontMetrics.ascent).toInt()
            // 用来设置基线的偏移量,再加上 getHeight() / 2 就是基线坐标，尼玛折腾了我好久
            mBaseLineDistance =
                Math.ceil(((fontMetrics.descent - fontMetrics.ascent) / 2 - fontMetrics.descent).toDouble())
                    .toInt().toFloat()
        }
        // 计算文字宽度
        if (TextUtils.isEmpty(mText)) {
            mText = ""
        }
        mFontLen = mPaint!!.measureText(mText).toInt()
        if (TextUtils.isEmpty(mTextChecked)) {
            mFontLenChecked = mFontLen
        } else {
            mFontLenChecked = mPaint!!.measureText(mTextChecked).toInt()
        }
        // 计算图标大小
        if ((mDecorateIcon != null || mDecorateIconChange != null) && mIconSize != mFontH) {
            mIconSize = mFontH
        }
        // 计算出了文字外所需要占用的宽度
        val allPadding: Int
        if (mTagMode == MODE_ICON_CHECK_INVISIBLE && mIsChecked) {
            allPadding = mHorizontalPadding * 2
        } else if (mDecorateIcon == null && !mIsChecked) {
            allPadding = mHorizontalPadding * 2
        } else {
            allPadding = mIconPadding + mIconSize + mHorizontalPadding * 2
        }
        // 设置显示的文字
        if (mIsChecked && !TextUtils.isEmpty(mTextChecked)) {
            if (mFontLenChecked + allPadding > maxWidth) {
                val pointWidth = mPaint!!.measureText(".")
                // 计算能显示的字体长度
                val maxTextWidth = maxWidth.toFloat() - allPadding.toFloat() - pointWidth * 3
                mShowText = _clipShowText(mTextChecked!!, mPaint!!, maxTextWidth)
                mFontLenChecked = mPaint!!.measureText(mShowText).toInt()
            } else {
                mShowText = mTextChecked
            }
        } else if (mFontLen + allPadding > maxWidth) {
            val pointWidth = mPaint!!.measureText(".")
            // 计算能显示的字体长度
            val maxTextWidth = maxWidth.toFloat() - allPadding.toFloat() - pointWidth * 3
            mShowText = _clipShowText(mText!!, mPaint!!, maxTextWidth)
            mFontLen = mPaint!!.measureText(mShowText).toInt()
        } else {
            mShowText = mText
        }

        return allPadding
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val allPadding = _adjustText(getMeasuredWidth())
        val fontLen = if (mIsChecked) mFontLenChecked else mFontLen
        // 如果为精确测量 MeasureSpec.EXACTLY，则直接使用测量的大小，否则让控件实现自适应
        // 如果你用了精确测量则 mHorizontalPadding 和 mVerticalPadding 会对最终大小判定无效
        val width = if (View.MeasureSpec.getMode(widthMeasureSpec) == View.MeasureSpec.EXACTLY)
            View.MeasureSpec.getSize(widthMeasureSpec)
        else
            allPadding + fontLen
        val height = if (View.MeasureSpec.getMode(heightMeasureSpec) == View.MeasureSpec.EXACTLY)
            View.MeasureSpec.getSize(heightMeasureSpec)
        else
            mVerticalPadding * 2 + mFontH
        setMeasuredDimension(width, height)
        // 计算图标放置位置
        if (mDecorateIcon != null || mDecorateIconChange != null) {
            val top = (height - mIconSize) / 2
            val left: Int
            if (mIconGravity == Gravity.RIGHT) {
                val padding = (width - mIconSize - fontLen - mIconPadding) / 2
                left = width - padding - mIconSize
            } else {
                left = (width - mIconSize - fontLen - mIconPadding) / 2
            }
            if (mTagMode == MODE_ICON_CHECK_CHANGE && mIsChecked && mDecorateIconChange != null) {
                mDecorateIconChange!!.setBounds(left, top, mIconSize + left, mIconSize + top)
            } else if (mDecorateIcon != null) {
                mDecorateIcon!!.setBounds(left, top, mIconSize + left, mIconSize + top)
            }
        }
    }

    protected override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        // 设置矩形边框
        mRect!!.set(mBorderWidth, mBorderWidth, w - mBorderWidth, h - mBorderWidth)
    }

    protected override fun onDraw(canvas: Canvas) {
        // 圆角
        var radius = mRadius
        if (mTagShape == SHAPE_ARC) {
            radius = mRect!!.height() / 2
        } else if (mTagShape == SHAPE_RECT) {
            radius = 0f
        }
        // 绘制背景
        mPaint!!.style = Paint.Style.FILL
        if (mIsChecked) {
            mPaint!!.color = mBgColorChecked
        } else {
            mPaint!!.color = mBgColor
        }
        canvas.drawRoundRect(mRect!!, radius, radius, mPaint!!)
        // 绘制边框
        mPaint!!.style = Paint.Style.STROKE
        mPaint!!.strokeWidth = mBorderWidth
        if (mIsChecked) {
            mPaint!!.color = mBorderColorChecked
        } else {
            mPaint!!.color = mBorderColor
        }
        canvas.drawRoundRect(mRect!!, radius, radius, mPaint!!)
        // 绘制文字
        mPaint!!.style = Paint.Style.FILL
        if (mIsChecked) {
            mPaint!!.color = mTextColorChecked
            val padding = if (mTagMode == MODE_ICON_CHECK_INVISIBLE) 0 else mIconSize + mIconPadding
            canvas.drawText(
                mShowText!!,
                (if (mIconGravity == Gravity.RIGHT)
                    (getWidth() - mFontLenChecked - padding) / 2
                else
                    (getWidth() - mFontLenChecked - padding) / 2 + padding).toFloat(),
                getHeight() / 2 + mBaseLineDistance, mPaint!!
            )
        } else {
            mPaint!!.color = mTextColor
            val padding = if (mDecorateIcon == null) 0 else mIconSize + mIconPadding
            canvas.drawText(
                mShowText!!,
                (if (mIconGravity == Gravity.RIGHT)
                    (getWidth() - mFontLen - padding) / 2
                else
                    (getWidth() - mFontLen - padding) / 2 + padding).toFloat(),
                getHeight() / 2 + mBaseLineDistance, mPaint!!
            )
        }
        // 绘制Icon
        if (mTagMode == MODE_ICON_CHECK_CHANGE && mIsChecked && mDecorateIconChange != null) {
            mDecorateIconChange!!.setColorFilter(mPaint!!.color, PorterDuff.Mode.SRC_IN)
            mDecorateIconChange!!.draw(canvas)
        } else if (mTagMode == MODE_ICON_CHECK_INVISIBLE && mIsChecked) {
            // Don't need to draw
        } else if (mDecorateIcon != null) {
            mDecorateIcon!!.setColorFilter(mPaint!!.color, PorterDuff.Mode.SRC_IN)
            mDecorateIcon!!.draw(canvas)
        }
        // 绘制半透明遮罩
        if (mIsPressed) {
            mPaint!!.color = mScrimColor
            canvas.drawRoundRect(mRect!!, radius, radius, mPaint!!)
        }
    }

    /**
     * ==================================== 触摸点击控制 ====================================
     */

    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (MotionEventCompat.getActionMasked(event)) {
            MotionEvent.ACTION_DOWN -> {
                mIsPressed = true
                invalidate()
            }
            MotionEvent.ACTION_MOVE -> if (mIsPressed && !_isViewUnder(event.x, event.y)) {
                mIsPressed = false
                invalidate()
            }
            MotionEvent.ACTION_UP -> {
                if (_isViewUnder(event.x, event.y)) {
                    _toggleTagCheckStatus()
                }
                if (mIsPressed) {
                    mIsPressed = false
                    invalidate()
                }
            }
            MotionEvent.ACTION_CANCEL -> if (mIsPressed) {
                mIsPressed = false
                invalidate()
            }
        }
        return super.onTouchEvent(event)
    }

    /**
     * 判断是否在 Tag 控件内
     *
     * @param x
     * @param y
     *
     * @return
     */
    private fun _isViewUnder(x: Float, y: Float): Boolean {
        return x >= 0 && x < getWidth() &&
                y >= 0 && y < getHeight()
    }

    /**
     * 切换tag选中状态
     */
    private fun _toggleTagCheckStatus() {
        if (mIsAutoToggleCheck) {
            setChecked(!mIsChecked)
        }
    }

    fun isChecked(): Boolean {
        return mIsChecked
    }

    /**
     * 设置选中状态
     *
     * @param checked
     */
    fun setChecked(checked: Boolean) {
        if (mIsChecked == checked) {
            return
        }
        mIsChecked = checked
        // 注意，这里用 requestLayout() 而不是只用 invalidate()，因为 invalidate() 没法回调 onMeasure() 进行测量，
        // 如果控件自适应大小的话是有可能改变大小的，所以加上 requestLayout()
        requestLayout()
        invalidate()
        if (mCheckListener != null) {
            mCheckListener!!.onCheckedChanged(mIsChecked)
        }
    }

    /**
     * 裁剪Text
     *
     * @param oriText
     * @param paint
     * @param maxTextWidth
     *
     * @return
     */
    private fun _clipShowText(oriText: String, paint: Paint, maxTextWidth: Float): String {
        var tmpWidth = 0f
        val strBuilder = StringBuilder()
        for (i in 0 until oriText.length) {
            val c = oriText[i]
            val cWidth = paint.measureText(c.toString())
            // 计算每个字符的宽度之和，如果超过能显示的长度则退出
            if (tmpWidth + cWidth > maxTextWidth) {
                break
            }
            strBuilder.append(c)
            tmpWidth += cWidth
        }
        // 末尾添加3个.并设置为显示字符
        strBuilder.append("...")
        return strBuilder.toString()
    }

    /**
     * ==================================== 接口 ====================================
     */

    fun getTagShape(): Int {
        return mTagShape
    }

    fun setTagShape(tagShape: Int) {
        mTagShape = tagShape
        _update()
    }

    fun getTagMode(): Int {
        return mTagMode
    }

    fun setTagMode(tagMode: Int) {
        mTagMode = tagMode
        _update()
    }

    fun getBgColor(): Int {
        return mBgColor
    }

    fun setBgColor(bgColor: Int) {
        mBgColor = bgColor
        // 设置颜色调用这个就行，回调onDraw()
        invalidate()
    }

    fun getBorderColor(): Int {
        return mBorderColor
    }

    fun setBorderColor(borderColor: Int) {
        mBorderColor = borderColor
        invalidate()
    }

    fun getTextColor(): Int {
        return mTextColor
    }

    fun setTextColor(textColor: Int) {
        mTextColor = textColor
        invalidate()
    }

    fun getBgColorChecked(): Int {
        return mBgColorChecked
    }

    fun setBgColorChecked(bgColorChecked: Int) {
        mBgColorChecked = bgColorChecked
        invalidate()
    }

    fun getBorderColorChecked(): Int {
        return mBorderColorChecked
    }

    fun setBorderColorChecked(borderColorChecked: Int) {
        mBorderColorChecked = borderColorChecked
        invalidate()
    }

    fun getTextColorChecked(): Int {
        return mTextColorChecked
    }

    fun setTextColorChecked(textColorChecked: Int) {
        mTextColorChecked = textColorChecked
        invalidate()
    }

    fun getScrimColor(): Int {
        return mScrimColor
    }

    fun setScrimColor(scrimColor: Int) {
        mScrimColor = scrimColor
        invalidate()
    }

    fun getTextSize(): Float {
        return mTextSize
    }

    fun setTextSize(textSize: Float) {
        mTextSize = textSize
    }

    fun getBorderWidth(): Float {
        return mBorderWidth
    }

    fun setBorderWidth(borderWidth: Float) {
        mBorderWidth = borderWidth
    }

    fun getRadius(): Float {
        return mRadius
    }

    fun setRadius(radius: Float) {
        mRadius = radius
        invalidate()
    }

    fun getText(): String? {
        return mText
    }

    fun setText(text: String) {
        mText = text
        _update()
    }

    fun getTextChecked(): String? {
        return mTextChecked
    }

    fun setTextChecked(textChecked: String) {
        mTextChecked = textChecked
        _update()
    }

    fun getHorizontalPadding(): Int {
        return mHorizontalPadding
    }

    fun setHorizontalPadding(horizontalPadding: Int) {
        mHorizontalPadding = horizontalPadding
        _update()
    }

    fun getVerticalPadding(): Int {
        return mVerticalPadding
    }

    fun setVerticalPadding(verticalPadding: Int) {
        mVerticalPadding = verticalPadding
        _update()
    }

    fun getDecorateIcon(): Drawable? {
        return mDecorateIcon
    }

    fun setDecorateIcon(decorateIcon: Drawable) {
        mDecorateIcon = decorateIcon
        _update()
    }

    fun getDecorateIconChange(): Drawable? {
        return mDecorateIconChange
    }

    fun setDecorateIconChange(decorateIconChange: Drawable) {
        mDecorateIconChange = decorateIconChange
        _update()
    }

    fun getIconPadding(): Int {
        return mIconPadding
    }

    fun setIconPadding(iconPadding: Int) {
        mIconPadding = iconPadding
        _update()
    }

    fun isAutoToggleCheck(): Boolean {
        return mIsAutoToggleCheck
    }

    fun setAutoToggleCheck(autoToggleCheck: Boolean) {
        mIsAutoToggleCheck = autoToggleCheck
    }

    /**
     * 调用这些接口进行属性设置如果最后可能会改变按钮的大小的话最后调用一下这个接口，以刷新界面，建议属性直接在布局里设置
     * 只需要回调onDraw()的话调用invalidate()就可以了
     */
    private fun _update() {
        requestLayout()
        invalidate()
    }

    /**
     * ==================================== 点击监听 ====================================
     */

    private var mCheckListener: OnCheckedChangeListener? = null


    fun setCheckListener(onCheckedChangeListener: OnCheckedChangeListener) {
        mCheckListener = onCheckedChangeListener
    }

    interface OnCheckedChangeListener {
        fun onCheckedChanged(isChecked: Boolean)
    }

    fun dp2px(context: Context, dp: Float): Float {
        return TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP, dp,
            context.resources.displayMetrics
        )
    }

    fun sp2px(context: Context, sp: Float): Float {
        return TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_SP, sp,
            context.resources.displayMetrics
        )
    }
}